/*
 Copyright (c) 2013- MeherTJ G.
 All rights reserved. Everybody can use these code freely.
 */
#pragma once

#include <string>
#include <cstdint>
#include "zen_hex.h"

namespace Zen
{
	namespace URLUtil
	{
		/**
		 alpha, number and the chars (-_.*): not change.
		 space : change to '+'
		 other characters: %xx
		 */
		std::string GetProtocol(std::string const& url);

		std::string Decode(void const* buf, size_t size);

		std::string Encode(void const* buf, size_t size);

		std::string Decode(std::string const&);

		std::string Encode(std::string const&);
	} // namespace URLUtil
} // namespace Zen

namespace Zen
{
	inline std::string URLUtil::Decode(const void* buf, size_t size)
	{
		std::string res;
		int state = 0;
		int v = 0;
		auto value = (unsigned char*)buf;
		for (size_t i = 0; i < size; i++)
		{
			if (state == 0)
			{
				if (value[i] == '%')
					state = 1;
				else if (value[i] == '+')
					res.push_back(0x20);
				else
					res.push_back(value[i]);
			}
			else if (state == 1)
			{
				v = Hex::DigitToValue(value[i]) << 4;
				state = 2;
			}
			else if (state == 2)
			{
				v |= Hex::DigitToValue(value[i]);
				res.push_back((unsigned char)v);
				state = 0;
			}
		}
		return res;
	}

	inline std::string URLUtil::Encode(void const* buf, size_t size)
	{
		std::string res;

		auto value = (unsigned char const*)buf;
		for (size_t i = 0; i < size; ++i)
		{
			auto c = value[i];
			if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '*')
			{
				res.push_back((char)c);
			}
			else if (c == 0x20)
			{
				res.push_back('+');
			}
			else
			{
				res.push_back('%');
				res.push_back(Hex::ValueToDigit(c >> 4));
				res.push_back(Hex::ValueToDigit(c & 0xf));
			}
		}
		return res;
	}

	inline std::string URLUtil::GetProtocol(std::string const& url)
	{
		auto pos = url.find(':');
		if (pos == url.npos) return "";
		for (decltype(pos) i = 0; i < pos; ++i)
		{
			if (!::isalnum(url[i])) return "";
		}
		return url.substr(0, pos);
	}

	inline std::string URLUtil::Decode(std::string const& value)
	{
		return Decode(value.data(), value.size());
	}

	inline std::string URLUtil::Encode(std::string const& value)
	{
		return Encode((void const*)value.data(), value.size());
	}
} // namespace Zen
